/*----------------------------------------------------------------------------------------
*
*  Copyright 2019, Gao Hai Hui, <fromzeropoint@126.com>.  All rights reserved.
*  https://gitee.com/helloworldghh/xoskit.git
*  Use of this source code is governed by a MIT license
*  that can be found in the License file.
*
----------------------------------------------------------------------------------------*/
#include "../import/head.h"
#include "../xos/head.h"
#include "file_impl.h"

/////////////////////////////////////////////////////////////////////////////////////////
// 
namespace xos
{

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //

    static file_impl::ITEM_DATA xos_item_data;

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //

    file_impl::file_impl()
    {
        init_data();
    }

    file_impl::~file_impl()
    {
        term();
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //

    int file_impl::get_xos_item_data( ITEM_DATA*& item_ptr )
    {
        int ret = 0;
        xos_item_data.set_container( mgr::container() );
        item_ptr = &xos_item_data;
        return ret;
    }

    int file_impl::init_data()
    {
        int nRet = 0;
        m_hFile = INVALID_HANDLE_VALUE;
        m_file_name = "";
        m_cTime = 0;
        m_aTime = 0;
        m_mTime = 0;
        return nRet;
    }

    int file_impl::init()
    {
        int nRet = 0;
        return nRet;
    }

    int file_impl::term()
    {
        int nRet = 0;

        close();
        init_data();

        return nRet;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //
    // open a file
    // 
    int file_impl::open( const char * lpszFileName, uint32_t ulAccessFlag, uint32_t ulShareMode, uint32_t ulCreateFlag )
    {
        int nRet = 0;

        uint32_t ulFileAccess = 0, ulFileOpen = 0, ulShare = 0;

        // access type

        if( ulAccessFlag & XOS_FILE_WRITE )
        {
            ulFileAccess |= GENERIC_WRITE;
            ulFileAccess |= GENERIC_READ;
        }

        if( ulAccessFlag & XOS_FILE_READ )
        {
            ulFileAccess |= GENERIC_READ;
        }

        // create type

        if( ulCreateFlag & XOS_FILE_CREATE )
        {
            ulFileOpen |= OPEN_ALWAYS;
        }

        if( ulCreateFlag & XOS_FILE_OPEN )
        {
            ulFileOpen |= OPEN_EXISTING;
        }

        if( ulCreateFlag & XOS_FILE_TRUNC )
        {
            ulFileOpen |= TRUNCATE_EXISTING;
        }

        // 
        // share type
        // 
        if( ulShareMode & XOS_SHARE_READ )
        {
            ulShare |= FILE_SHARE_READ;
        }

        if( ulShareMode & XOS_SHARE_WRITE )
        {
            ulShare |= FILE_SHARE_WRITE;
        }

        // 
        // use unicode in windows
        // 
        {
            m_hFile = CreateFileA( 
                lpszFileName, 
                ulFileAccess,
                ulShare,
                0,
                ulFileOpen,
                FILE_ATTRIBUTE_NORMAL,
                0 );
        }

        if( INVALID_HANDLE_VALUE == m_hFile )
        {
            //DWORD dwErr = GetLastError();
            nRet = 1;
        }

        return nRet;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //

    // 
    // get file time
    // 
    int file_impl::get_file_time( int64_t * lpCreateTime, int64_t * lpAccessTime, int64_t * lpModifyTime )
    {
        int ret = 0;

        FILETIME fCreateTime, fAccessTime, fModifyTime;

        if( INVALID_HANDLE_VALUE == m_hFile )
        {
            if( lpCreateTime )
            {
                *lpCreateTime = m_cTime;
            }
            if( lpAccessTime )
            {
                *lpAccessTime = m_aTime;
            }
            if( lpModifyTime )
            {
                *lpModifyTime = m_mTime;
            }
            return 1;
        }

        if( !GetFileTime( m_hFile, &fCreateTime, &fAccessTime, &fModifyTime ) )
        {
            return 2;
        }

        if( lpCreateTime )
        {
            int64_t sz = fCreateTime.dwHighDateTime;
            sz = ( sz << 32 ) | fCreateTime.dwLowDateTime;
            *lpCreateTime = sz;
            m_cTime = sz;
        }

        if( lpAccessTime )
        {
            int64_t sz = fAccessTime.dwHighDateTime;
            sz = ( sz << 32 ) | fAccessTime.dwLowDateTime;
            *lpAccessTime = sz;
            m_aTime = sz;
        }

        if( lpModifyTime )
        {
            int64_t sz = fModifyTime.dwHighDateTime;
            sz = ( sz << 32 ) | fModifyTime.dwLowDateTime;
            *lpModifyTime = sz;
            m_mTime = sz;
        }

        return ret;
    }

    // 
    // get file or dir name
    // 
    const char * file_impl::get_file_name( char * lpszFileName, int nLen, int * pnLenNeeded )
    {
        int len = ( int )m_file_name.length();

        if( pnLenNeeded )
        {
            *pnLenNeeded = len;
        }

        if( lpszFileName && ( nLen > len ) )
        {
            strcpy_s( lpszFileName, nLen, m_file_name.c_str() );
        }

        return m_file_name.c_str();
    }

    // 
    // move file pointer to ...
    // 
    int file_impl::set_file_pos( enumFilePos enumFrom, int64_t iPos64, int64_t * lpSeek64 )
    {
        int nRet = 0;

        LARGE_INTEGER llToMove = { {0} }, llPos = { {0} };
        DWORD dwType = 0;
        llPos.QuadPart = -1;

        if( 0 == nRet )
        {
            switch( enumFrom )
            {
            case XOS_FILE_POS_BEGIN:
                {
                    dwType = FILE_BEGIN;
                }
                break;
            case XOS_FILE_POS_CUR:
                {
                    dwType = FILE_CURRENT;
                }
                break;
            case XOS_FILE_POS_END:
                {
                    dwType = FILE_END;
                }
                break;
            default:
                {
                    nRet = 1;
                }
                break;
            }
        }

        if( 0 == nRet )
        {
            if( INVALID_HANDLE_VALUE == m_hFile )
            {
                nRet = 1;
            }
        }

        if( 0 == nRet )
        {
            llToMove.QuadPart = iPos64;

            if( !SetFilePointerEx( m_hFile, llToMove, &llPos, dwType ) )
            {
                nRet = 1;
            }
        }

        if( lpSeek64 )
        {
            *lpSeek64 = llPos.QuadPart;
        }

        return nRet;
    }

    // 
    // get current file pointer
    // 
    int file_impl::get_file_pos( int64_t * lpSize64 )
    {
        int nRet = 0;

        LARGE_INTEGER llToMove = { {0} }, llPos = { {0} };

        if( INVALID_HANDLE_VALUE == m_hFile )
        {
            nRet = 1;
        }

        if( lpSize64 )
        {
            *lpSize64 = 0;
        }

        if( 0 == nRet )
        {
            if( !SetFilePointerEx( m_hFile, llToMove, &llPos, FILE_BEGIN ) )
            {
                nRet = 1;
            }
        }

        if( 0 == nRet )
        {
            if( lpSize64 )
            {
                *lpSize64 = llPos.QuadPart;
            }
        }

        return nRet;
    }

    // 
    // get file size
    // 
    int file_impl::get_file_size( int64_t * lpSize64 )
    {
        int nRet = 0;

        LARGE_INTEGER llSize = { {0} };

        if( INVALID_HANDLE_VALUE == m_hFile )
        {
            nRet = 1;
        }

        if( lpSize64 )
        {
            *lpSize64 = 0L;
        }

        if( 0 == nRet )
        {
            if( !GetFileSizeEx( m_hFile, &llSize ) )
            {
                nRet = 1;
            }
        }

        if( 0 == nRet )
        {
            if( lpSize64 )
            {
                *lpSize64 = llSize.QuadPart;
            }
        }

        return nRet;
    }

    // 
    // commit file disk space.
    // 
    int file_impl::commit_file( int64_t iPos64 )
    {
        int nRet = 0;

        LARGE_INTEGER ulSize = { {0} };
        void * pView = 0;
        HANDLE hMap = 0;

        ulSize.QuadPart = ( LONGLONG )iPos64;

        if( 0 == nRet )
        {
            hMap = CreateFileMapping( m_hFile, 0, PAGE_READWRITE, ulSize.HighPart, ulSize.LowPart, 0 );

            if( !hMap )
            {
                nRet = 1;
            }
        }

        if( 0 == nRet )
        {
            pView = MapViewOfFile( hMap, FILE_MAP_WRITE | FILE_MAP_READ, 0, 0, 0 );
        }

        if( pView )
        {
            UnmapViewOfFile( pView );
            pView = 0;
        }

        if( hMap )
        {
            CloseHandle( hMap );
            hMap = 0;
        }

        return nRet;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //

    // 
    // write file data
    // 
    // -1 means failed. other , write bytes
    // 
    int file_impl::write( const void * lpBuf, uint32_t ulBufSize )
    {
        int nRet = 0;

        if( INVALID_HANDLE_VALUE == m_hFile )
        {
            nRet = -1;
        }

        if( 0 == nRet )
        {
            DWORD dwLen = 0;

            if( WriteFile( m_hFile, lpBuf, ulBufSize, &dwLen, 0 ) )
            {
                nRet = dwLen;
            }
            else
            {
                nRet = -1;
            }
        }

        return nRet;
    }

    // 
    // read file data
    // 
    // -1 means failed. other , read bytes
    //
    int file_impl::read( void * lpBuf, uint32_t ulBufSize )
    {
        int nRet = 0;

        if( INVALID_HANDLE_VALUE == m_hFile )
        {
            nRet = -1;
        }

        if( 0 == nRet )
        {
            DWORD dwLen = 0;

            if( ReadFile( m_hFile, lpBuf, ulBufSize, &dwLen, 0 ) )
            {
                nRet = dwLen;
            }
            else
            {
                nRet = -1;
            }
        }

        return nRet;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //

    int file_impl::write( xos_box::i_list * pList )
    {
        int ret = 0;
        xos_box::i_list_iter * pIter = pList->begin();
        xos_box::i_buf * pBuf = 0;
        while( ( pBuf = (xos_box::i_buf*)pIter->next() ) )
        {
            int len = write( pBuf );
            ret += len;
        }
        xos_stl::release_interface( pIter );
        return ret;
    }

    int file_impl::read( xos_box::i_list * pList )
    {
        int ret = 0;
        xos_box::i_buf * pBuf = 0;
        while( 1 )
        {
            pBuf = mgr::container()->box()->buf();
            int len = read( pBuf );
            if( len > 0 )
            {
                pList->push_back( pBuf );
                ret += len;
                pBuf = 0;
            }
            else
            {
                xos_stl::release_interface( pBuf );
                break;
            }
        }
        return ret;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //

    int file_impl::write( xos_box::i_big_buf * pBuf )
    {
        int nRet = 0;
        int nLen = 0;
        char * lpszBuf = pBuf->get_data( 0, 0, 0, &nLen );
        nRet = write( lpszBuf, (uint32_t)nLen );
        return nRet;
    }

    int file_impl::read( xos_box::i_big_buf * pBuf )
    {
        int nRet = 0;
        int nLen = pBuf->get_len( 0 );
        int nPos = pBuf->get_pos( 0 );
        uint32_t uSize = (uint32_t)( pBuf->get_buf_size( 0 ) - nLen - nPos );
        char * lpszBuf = pBuf->get_data( 0, 0, 0, 0 );
        nRet = read( lpszBuf, uSize );
        if( nRet > 0 )
        {
            nLen += nRet;
            pBuf->set_len( nLen );
        }
        return nRet;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //

    int file_impl::write( xos_box::i_buf * pBuf )
    {
        int nRet = 0;
        int nLen = 0;
        char * lpszBuf = pBuf->get_data( 0, 0, 0, &nLen );
        nRet = write( lpszBuf, (uint32_t)nLen );
        return nRet;
    }

    int file_impl::read( xos_box::i_buf * pBuf )
    {
        int nRet = 0;
        int nLen = pBuf->get_len( 0 );
        int nPos = pBuf->get_pos( 0 );
        uint32_t uSize = (uint32_t)( pBuf->get_buf_size( 0 ) - nLen - nPos );
        char * lpszBuf = pBuf->get_data( 0, 0, 0, 0 );
        nRet = read( lpszBuf, uSize );
        if( nRet > 0 )
        {
            nLen += nRet;
            pBuf->set_len( nLen );
        }
        return nRet;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //

    // 
    // close file handle
    //  
    int file_impl::close()
    {
        int nRet = 0;

        if( INVALID_HANDLE_VALUE != m_hFile )
        {
            CloseHandle( m_hFile );
            m_hFile = INVALID_HANDLE_VALUE;
        }

        return nRet;
    }
} // xos
